//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public License
// along with this program.  If not, see http://www.gnu.org/licenses/.
// 

#include "ProposalIIISelective.h"

Define_Module(ProposalIIISelective);

void ProposalIIISelective::initialize()
{
    numOfFrames = getAncestorPar("num_of_frames");
    ARQ_Block_Size = getAncestorPar("arq_block_size");//(bytes)
    PDU_Size = getAncestorPar("pdu_size");//(blocks)
    frameSize = getAncestorPar("data_size");
    blerstop = getAncestorPar("bstop");
    BLER = getAncestorPar("bstart");

    numOfBlocks = frameSize/ARQ_Block_Size;

    overhead = 0;
    statIndex = 0;

    for(int j=0; j<21 ; j++)
        statTable[j] = 0;
}

void ProposalIIISelective::handleMessage(cMessage *msg)
{
    if(dynamic_cast<Data *>(msg))
        handleData(check_and_cast<Data *>(msg));
}

void ProposalIIISelective::handleData(Data *msg)
{
    if(msg->getBLERup()==true)
    {
        statisticUpdate();
        if(BLER > blerstop)
            return;                             // end of the simulation
        else
        {
            ARQ_pck *feedback = new ARQ_pck("Ready");
            feedback->setLast(true);            // dummyMessage
            send(feedback,"toBase");
            return;
        }
    }
    else
    {
        int stop = msg->getBlocksArraySize();
        for(int i=0 ; i<stop; i++)
            vect.push_back(msg->getBlocks(i));
    }
    if(msg->getLast())
    {
        EV<<"Received Blocks are: ";
        for (unsigned i=0; i<vect.size() ; i++)
            EV<<vect.at(i)<<" ";
        EV<<endl;
        reply();
    }
    delete(msg);
}

void ProposalIIISelective::reply()
{
    int blockNum;
    double prob;
    int size = vect.size();
    int B = 0;

    ARQ_pck *feedback = new ARQ_pck("ACK");
    feedback->setType(selectiveANDcumulative);
    feedback->setRcvdBlocksArraySize(size);

    for(int i=0 ; i<size; i++)
    {
        blockNum = vect.at(i);
        prob = uniform(0,100);

        if(prob < BLER)
        {
            if(B==0)
                curBlock = blockNum-1;
            B++;
            feedback->setRcvdBlocks(i, blockNum);
        }
        else
            feedback->setRcvdBlocks(i, -1);         //if the block has error its number will be set as positive
    }

    if(B>0)//error happened
    {
        int M = (B%16==0) ? B/16 : (B/16) + 1;
        int min = minimum(16 + 16*M , 10 + (B*11)); //min ---> (8)

        int res = (8 - (8 + 18 + min)%8)%8;
//        EV<<"overhead = currentOH("<<overhead<<") + 8 + 18 + min("<<min<<") + res("<<res<<") = ";
        overhead += 8 + 18 + min + res;             //Size ARQ FB_III --> (8)
        EV<<overhead<<endl;
    }
    else
    {
        curBlock = numOfBlocks-1;
        feedback->setLast(true);
    }

    feedback->setBSN(curBlock);
    vect.clear();
    printMessage(feedback);
    send(feedback,"toBase");
}

void ProposalIIISelective::printMessage(ARQ_pck *msg)
{
    bool wereErrors = false;
    int stop = msg->getRcvdBlocksArraySize();
    ostringstream oss;
    oss<<"Error in blocks -> { ";

    for(int i=0 ; i<stop ; i++)
    {
        int block = msg->getRcvdBlocks(i);
        if(block>=0)
        {
            wereErrors = true;
            oss<<block<<" ";
        }
    }
    oss<<"}";
    EV<<"BSN set to --> "<<curBlock<<". ";
    if(wereErrors)
        EV<<oss.str()<<endl;
    else
        EV<<"No errors found"<<endl;
}

int ProposalIIISelective::minimum(int x, int y)
{
    return x<y ? x : y ;
}

void ProposalIIISelective::statisticUpdate()
{
    int temp = overhead/1024 + 0.5;           //overhead in KiloBits
    int type = getAncestorPar("simtype");
    statTable[statIndex++]= temp;
    overhead = 0;
    if(type==1)
    {
        BLER += 1;
        EV<<"BLER is up to ------------------------------> "<<BLER<<"%"<<endl;
    }
    else
    {
        if(ARQ_Block_Size==16)
            ARQ_Block_Size=64;
        else if(ARQ_Block_Size==64)
            ARQ_Block_Size=128;
        else if(ARQ_Block_Size==128)
            ARQ_Block_Size=256;
        else if(ARQ_Block_Size==256)
            ARQ_Block_Size=512;
        else if(ARQ_Block_Size==512)
            ARQ_Block_Size=1024;
        else
            BLER += 1;
    }
}

void ProposalIIISelective::finish()
{
    blerstop -= 0.1;
    ostringstream oss;
    oss<<"Results ARQ"<<ARQ_Block_Size<<" Frame"<<frameSize<<".txt";
    string fname = oss.str();
    myfile.open (fname.c_str(),ios::app);
    myfile<<getClassName()<<" ARQ "<<ARQ_Block_Size<<endl;
    EV<<"---------------------------------------"<<endl;
    EV<<"Table of BLER to  Overhead in: "<<getClassName()<<endl;
    EV<<"Number of Frames: "<<numOfFrames<<" ; ARQ Block Size: "<<ARQ_Block_Size<<" B ; ";
    EV<<"PDU Size: "<<PDU_Size<<" blocks; Frame Size: "<<frameSize<<" B"<<endl;
    EV<<"---------------------------------------"<<endl;
    int type = getAncestorPar("simtype");
    if(type==1)
    {
        for(int j=0; j<statIndex ; j++)
        {
            myfile<<statTable[j]<<endl;
            EV<<statTable[j]<<endl;
        }
    }
    else
    {
        EV<<"BLER --> "<<blerstop<<"%"<<endl;
        EV<<"---------------------------------------"<<endl;
        EV<<statTable[0]<<endl;
        EV<<statTable[1]<<endl;
        EV<<statTable[2]<<endl;
        EV<<statTable[3]<<endl;
        EV<<statTable[4]<<endl;
        EV<<statTable[5]<<endl;
    }
    myfile.close();
    EV<<"---------------------------------------"<<endl;
}
